﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GameLibrary.Map
{
    using Interfaces;
    [Serializable]
    public class Region<TSettlement,TUnit>
        where TSettlement :class,IMapObject
        where TUnit :class,IMapObject
    {
        #region variables

        /// <summary>
        /// Where this region starts.
        /// </summary>
        public Position Start { get; private set; }

        /// <summary>
        /// All the settlement or settlementinfo in this region.
        /// </summary>
        private MapObjects SettlementList = new MapObjects();

        /// <summary>
        /// All the units of unitinfos in this region.
        /// </summary>
        private MapObjects UnitList = new MapObjects();

        public bool HaveFreeRegions
        {
            get
            {
                if (this.SettlementList.Count < GMHelper.RegionSize)
                    return true;
                else
                    return false;
            }
        }

        #endregion

        #region constructor

        public Region(Position start)
        {
            this.Start = start;
        }

        public Region(Region<TSettlement, TUnit> region)
        {
            this.Start = region.Start;
            this.UnitList = new MapObjects(region.UnitList);
            this.SettlementList = new MapObjects(region.SettlementList);
        }

        #endregion

        #region add

        public virtual bool AddUnit(TUnit unit)
        {
            return this.UnitList.Add(unit);
        }

        public virtual bool AddSettlement(TSettlement settlement)
        {
            return this.SettlementList.Add(settlement);
        }

        #endregion

        #region remove

        public bool RemoveUnit(uint unitID)
        {
            return this.UnitList.Remove(unitID);
        }

        public bool RemoveSettlement(uint settlementID)
        {
            return this.SettlementList.Remove(settlementID);
        }

        /// <summary>
        /// Clear everything stored
        /// </summary>
        public void DeleteAll()
        {
            this.SettlementList.DeleteAll();
            this.UnitList.DeleteAll();
        }

        #endregion

        #region get

        /// <summary>
        /// Return null if no free positions for building villages.
        /// Else return free positions.
        /// </summary>
        /// <returns></returns>
        public List<Position> GetFreePosition()
        {
            if (this.SettlementList.Count == GMHelper.RegionSize)
                return null;
            else
            {
                List<Position> list = new List<Position>();
                for (int i = 0; i < GMHelper.RegionWidth; i++)
                    for (int j = 0; j < GMHelper.RegionHeight; j++)
                    {
                        Position pos = Start + new Position(i,j);
                        if (pos.X >= GMHelper.Width
                            || pos.Y >= GMHelper.Height
                            )
                            continue;
                        if (this.SettlementList.ContainsPosition(pos) == false)
                            list.Add(pos);
                    }
                return list;
            }
        }

        public TSettlement GetSettlementByID(uint settlementID)
        {
                return (TSettlement)this.SettlementList[settlementID];
        }

        public TSettlement GetSettlementByPosition(Position pos)
        {
            var settlements = this.SettlementList.GetObjsByPosition(pos);
            if (settlements != null)
                return settlements.First() as TSettlement;
            else
                return null;
        }

        public List<TSettlement> GetSettlementByOwner(uint ownerID)
        {
            List<TSettlement> list = new List<TSettlement>();
            foreach (var settlement in this.SettlementList.GetObjsByOwnerID(ownerID))
                list.Add((TSettlement)settlement);
            return list;
        }

        public TUnit GetUnitByID(uint unitID)
        {
            return (TUnit)this.UnitList[unitID];
        }

        public List<TUnit> GetUnitsByPosition(Position pos)
        {
            List<TUnit> list = new List<TUnit>();
            var ulist = this.UnitList.GetObjsByPosition(pos);
            if(ulist == null)
                return list;
            foreach (var u in ulist)
                list.Add((TUnit)u);
            return list;
        }

        public List<TUnit> GetUnitsByOwnership(uint ownerID)
        {
            List<TUnit> list = new List<TUnit>();
            foreach (var u in this.UnitList.GetObjsByOwnerID(ownerID))
                list.Add((TUnit)u);
            return list;
        }

        public IEnumerable<TUnit> GetUnits()
        {
            foreach (var u in this.UnitList.Values)
                yield return (TUnit)u;
        }
        public IEnumerable<uint> GetUnitIDs()
        {
            foreach (var u in this.UnitList.Values)
                yield return u.ID;
        }

        public IEnumerable<TSettlement> GetSettlements()
        {
            foreach (var settlement in this.SettlementList.Values)
                yield return (TSettlement)settlement;
        }

        public IEnumerable<uint> GetSettlementIDs()
        {
            foreach (var settlement in this.SettlementList.Values)
                yield return settlement.ID;
        }

        public HashSet<uint> GetOwnerList()
        {
            HashSet<uint> ownerIDs = new HashSet<uint>();
            ownerIDs.AddRange(this.SettlementList.GetOwnerIDs());
            ownerIDs.AddRange(this.UnitList.GetOwnerIDs());
            return ownerIDs;
        }

        #endregion

        #region contains

        public bool ContainsSettlementID(uint settlementID)
        {
            return this.SettlementList.ContainsID(settlementID);
        }

        public bool ContainsSettlementOwnerID(uint settlementOwnerID)
        {
            return this.SettlementList.ContainsOwner(settlementOwnerID);
        }

        public bool ContainsSettlementPosition(Position settlementPos)
        {
            return this.SettlementList.ContainsPosition(settlementPos);
        }

        public bool ContainsUnitID(uint unitID)
        {
            return this.UnitList.ContainsID(unitID);
        }

        public bool ContainsUnitOwnerID(uint unitOwnerID)
        {
            return this.UnitList.ContainsOwner(unitOwnerID);
        }

        public bool ContainsUnitPosition(Position unitPosition)
        {
            return this.UnitList.ContainsPosition(unitPosition);
        }

        #endregion
    }
}
